﻿using System;

namespace System.Collections.Generic
{
    /// <summary>
    /// 字典扩展方法类
    /// </summary>
    /// <remarks>
    /// 提供了一组用于简化字典操作的扩展方法，包括：
    /// - 获取或添加值
    /// - 安全获取值（带默认值）
    /// </remarks>
    public static class DictionaryExtensions
    {
        /// <summary>
        /// 获取指定键的值，如果键不存在则使用工厂方法添加新值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="dictionary">要操作的字典</param>
        /// <param name="key">要获取或添加的键</param>
        /// <param name="factory">用于创建新值的工厂方法</param>
        /// <returns>获取到的现有值或新创建的值</returns>
        /// <exception cref="ArgumentNullException">当dictionary或factory为null时抛出</exception>
        public static TValue GetOrAdd<TKey, TValue>(
            this IDictionary<TKey, TValue> dictionary,
            TKey key,
            Func<TValue> factory)
        {
            ArgumentNullException.ThrowIfNull(dictionary);
            ArgumentNullException.ThrowIfNull(factory);

            return dictionary.GetOrAdd(key, _ => factory());
        }

        /// <summary>
        /// 获取指定键的值，如果键不存在则使用基于键的工厂方法添加新值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="dictionary">要操作的字典</param>
        /// <param name="key">要获取或添加的键</param>
        /// <param name="factory">基于键创建新值的工厂方法</param>
        /// <returns>获取到的现有值或新创建的值</returns>
        /// <exception cref="ArgumentNullException">当dictionary或factory为null时抛出</exception>
        public static TValue GetOrAdd<TKey, TValue>(
            this IDictionary<TKey, TValue> dictionary,
            TKey key,
            Func<TKey, TValue> factory)
        {
            ArgumentNullException.ThrowIfNull(dictionary);
            ArgumentNullException.ThrowIfNull(factory);

            if (dictionary.TryGetValue(key, out var existingValue))
            {
                return existingValue;
            }

            var newValue = factory(key);
            dictionary[key] = newValue;
            return newValue;
        }

        /// <summary>
        /// 安全获取字典中指定键的值，如果键不存在则返回默认值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="dictionary">要查询的字典</param>
        /// <param name="key">要获取值的键</param>
        /// <returns>找到的值，如果键不存在则返回默认值</returns>
        /// <remarks>此重载专门用于非空键的字典</remarks>
        /// <exception cref="ArgumentNullException">当dictionary为null时抛出</exception>
        public static TValue? GetOrDefault<TKey, TValue>(
            this Dictionary<TKey, TValue> dictionary,
            TKey key) where TKey : notnull
        {
            ArgumentNullException.ThrowIfNull(dictionary);

            return dictionary.TryGetValue(key, out var value) ? value : default;
        }

        /// <summary>
        /// 安全获取字典接口中指定键的值，如果键不存在则返回默认值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="dictionary">要查询的字典接口</param>
        /// <param name="key">要获取值的键</param>
        /// <returns>找到的值，如果键不存在则返回默认值</returns>
        /// <exception cref="ArgumentNullException">当dictionary为null时抛出</exception>
        public static TValue? GetOrDefault<TKey, TValue>(
            this IDictionary<TKey, TValue> dictionary,
            TKey key)
        {
            ArgumentNullException.ThrowIfNull(dictionary);

            return dictionary.TryGetValue(key, out var value) ? value : default;
        }

        /// <summary>
        /// 安全获取只读字典中指定键的值，如果键不存在则返回默认值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="dictionary">要查询的只读字典</param>
        /// <param name="key">要获取值的键</param>
        /// <returns>找到的值，如果键不存在则返回默认值</returns>
        /// <exception cref="ArgumentNullException">当dictionary为null时抛出</exception>
        public static TValue? GetOrDefault<TKey, TValue>(
            this IReadOnlyDictionary<TKey, TValue> dictionary,
            TKey key)
        {
            ArgumentNullException.ThrowIfNull(dictionary);

            return dictionary.TryGetValue(key, out var value) ? value : default;
        }
    }
}